package tech

import "ssp/set"

func (s *Solve) RectangleElimination(g *Grid) (used bool, results []*Result) {
	for cand := range 9 {
		for _, row := range g.lines {
			if ok, rs := g.eliminationCandHouse(cand, row); ok {
				if OUTPUTRESULT {
					results = append(results, rs...)
				}
				used = true
				if !EXHAUSTIVE {
					return
				}
			}
		}
	}
	return
}

func (g *Grid) eliminationCandHouse(cand int, line *House) (used bool, results []*Result) {
	chain := getStrongChainHouse(cand, line)
	if len(chain) == 0 {
		return
	}
	if chain[0].boxNum == chain[1].boxNum {
		return
	}
	for i, p0 := range chain {
		p1 := chain[1-i]
		p2s := g.getWeakChainWithCand(cand, line.kind, p1)
		for _, p2 := range p2s {
			ok, p3s := g.verifyRectElimination(cand, line.kind, p0, p2)
			if !ok {
				continue
			}
			//fmt.Println(ok, p0, p1, p2, p3)
			//p2.removeCand(cand)
			used = true
			if OUTPUTRESULT {
				result := new(Result)
				result.addRemoved(set.NewSet(cand), []*Cell{p2})
				g1 := result.getHelpCandsGroup()
				g1.add(set.NewSet(cand), chain)
				g2 := result.getHelpCandsGroup()
				g2.add(set.NewSet(cand), p3s)
				result.addHelpHouses([]*House{&g.boxes[p3s[0].boxNum]})
				result.setStrategyName("Rectangle Elimination")
				results = append(results, result)
			}
			if CHANGE {
				p2.removeCand(cand)
			}
		}
	}
	return
}

func getStrongChainHouse(cand int, line *House) (chain []*Cell) {
	count := 0
	for _, cell := range line.cells {
		if cell.cands.Has(cand) {
			count++
			if count > 2 {
				break
			}
			chain = append(chain, cell)
		}
	}
	if count == 2 {
		return chain
	}
	return []*Cell{}
}

func (g *Grid) getWeakChainWithCand(cand, kind int, strongEnd *Cell) (weakCells []*Cell) {
	var line House
	switch kind {
	case 0:
		line = g.cols[strongEnd.colNum]
	case 1:
		line = g.rows[strongEnd.rowNum]
	default:
		return []*Cell{}
	}

	for _, cell := range line.cells {
		if cell.solved() {
			continue
		}
		if cell.cands.Has(cand) && cell.boxNum != strongEnd.boxNum {
			weakCells = append(weakCells, cell)
		}
	}
	return
}

func (g *Grid) verifyRectElimination(cand, kind int, p0, p2 *Cell) (ok bool, p3s []*Cell) {
	var rowNum, colNum int
	switch kind {
	case 0:
		rowNum = p2.rowNum
		colNum = p0.colNum
	case 1:
		rowNum = p0.rowNum
		colNum = p2.colNum
	}
	p3 := g.rows[rowNum].cells[colNum]
	//fmt.Println(p0, p1, p2, p3)
	for _, cell := range g.boxes[p3.boxNum].cells {
		//fmt.Println(cell)
		if cell.value == toValue(cand) {
			return
		}
		if cell.rowNum != rowNum && cell.colNum != colNum {
			if cell.cands.Has(cand) {
				return
			}
		} else if cell.cands.Has(cand) {
			p3s = append(p3s, cell)
		}
	}
	ok = true

	return
}
